package com.senyint.hip.fhir.provider;

import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.r4.model.*;

import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.Delete;
import ca.uhn.fhir.rest.annotation.History;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * *******description*******
 * 报告（文档）
 * *************************
 *
 * @author ChenPan
 * @date 2018/10/29 10:53
 */
@Component
public class BundleProvider implements IResourceProvider {

    /**
     * 临时存储数据
     */
    private Map<Long, Deque<Bundle>> bundleRes = new HashMap<Long, Deque<Bundle>>();

    private long bundleIndex = 1;

    @Override
    public Class<? extends IBaseResource> getResourceType() {
        return Bundle.class;
    }

    @Search
    public List<Bundle> findBundlesUsingArbitraryCtriteria() {
        LinkedList<Bundle> retVal = new LinkedList<Bundle>();

        for (Deque<Bundle> nextBundleList : bundleRes.values()) {
            Bundle nextBundle = nextBundleList.getLast();
            retVal.add(nextBundle);
        }

        return retVal;
    }

    @Create()
    public MethodOutcome transaction(RequestDetails theRequestDetails, @ResourceParam Bundle theResources) {
        long id = bundleIndex++;
        String versionId = addNewVersion(theResources, id);
        MethodOutcome retVal = new MethodOutcome();
        retVal.setId(new IdType("Bundle", String.valueOf(id), versionId));

        OperationOutcome outcome = new OperationOutcome();
        outcome.setIdElement(new IdType("Bundle", String.valueOf(id), versionId));
        outcome.addIssue().setDiagnostics("新增Bundle成功").setId(String.valueOf(id));
        retVal.setOperationOutcome(outcome);
        return retVal;
    }


    private String addNewVersion(Bundle theBundle, Long theId) {
        Date publishedDate;
        if (!bundleRes.containsKey(theId)) {
            bundleRes.put(theId, new LinkedList<Bundle>());
            publishedDate = InstantDt.withCurrentTime().getValue();
        } else {
            Bundle currentBundle = bundleRes.get(theId).getLast();
            publishedDate = currentBundle.getMeta().getLastUpdated();
        }

        /*
         * PUBLISHED time will always be set to the time that the first version was stored. UPDATED time is set to the time that the new version was stored.
         */
        theBundle.getMeta().setLastUpdated(publishedDate);
        theBundle.getMeta().setLastUpdated(InstantDt.withCurrentTime().getValue());
        Deque<Bundle> existingVersions = bundleRes.get(theId);

        // We just use the current number of versions as the next version number
        String newVersion = Integer.toString(existingVersions.size());

        // Create an ID with the new version and assign it back to the resource
        IdType newId = new IdType("Bundle", Long.toString(theId), newVersion);
        theBundle.setId(newId);

        existingVersions.add(theBundle);

        return newVersion;
    }

    @Search()
    public List<Bundle> findBundlesByCodeAndName(@OptionalParam(name = "itemCode") String itemCode, @OptionalParam(name = "itemName") String itemName,@OptionalParam(name = "_id") String id) {
        LinkedList<Bundle> retVal = new LinkedList<Bundle>();

        if(StringUtils.isEmpty(itemCode)&&StringUtils.isEmpty(itemName)&&StringUtils.isEmpty(id)) {
        	return findBundlesUsingArbitraryCtriteria();
        }
        
        /*
         * Look for all Bundles matching the name
         */
        for (Deque<Bundle> nextBundleList : bundleRes.values()) {
            Bundle nextBundle = nextBundleList.getLast();
            Long idPartAsLong = nextBundle.getIdElement().getIdPartAsLong();
            NAMELOOP:
            for (Bundle.BundleEntryComponent entry : nextBundle.getEntry()) {
                if (entry.getResource() instanceof Observation) {
                    Observation obs = (Observation) entry.getResource();
                    if (StringUtils.isNoneBlank(itemCode)) {
                        if (StringUtils.isNoneBlank(itemName)) {
                        	if(StringUtils.isNoneBlank(id)) {
                        		if (itemCode.equals(obs.getCode().getCoding().get(0).getCode()) && itemName.equals(obs.getCode().getText()) && id.equals(idPartAsLong.toString())) {
                                    retVal.add(nextBundle);
                                    break NAMELOOP;
                                }
                        	}else {
                            if (itemCode.equals(obs.getCode().getCoding().get(0).getCode()) && (itemName.equals(obs.getCode().getText())||itemName.equals(obs.getCode().getCoding().get(0).getDisplay()))) {
                                retVal.add(nextBundle);
                                break NAMELOOP;
                            }
                        	}
                        } else {
                            if (itemCode.equals(obs.getCode().getCoding().get(0).getCode())) {
                                retVal.add(nextBundle);
                                break NAMELOOP;
                            }
                        }
                    } else if (StringUtils.isNoneBlank(itemName) && StringUtils.isBlank(itemCode)) {
                    	if(StringUtils.isNoneBlank(id)) {
                    		if ((itemName.equals(obs.getCode().getCoding().get(0).getDisplay())||itemName.equals(obs.getCode().getText()))&&id.equals(idPartAsLong.toString())) {
                                retVal.add(nextBundle);
                                break NAMELOOP;
                            }
                    	}else {
                        if (itemName.equals(obs.getCode().getCoding().get(0).getDisplay())||itemName.equals(obs.getCode().getText())) {
                            retVal.add(nextBundle);
                            break NAMELOOP;
                        }
                    	}
                    }else if(StringUtils.isNoneBlank(id)) {
                    	  if (id.equals(idPartAsLong.toString())) {
                              retVal.add(nextBundle);
                              break NAMELOOP;
                          }
                    }
                }
            }
        }

        return retVal;
    }

    @Read(version = true)
    public Bundle readBundle(@IdParam IdType theId) {
        Deque<Bundle> retVal;
        try {
            retVal = bundleRes.get(theId.getIdPartAsLong());
        } catch (NumberFormatException e) {
            /*
             * If we can't parse the ID as a long, it's not valid so this is an unknown resource
             */
            throw new ResourceNotFoundException(theId);
        }

        if (theId.hasVersionIdPart() == false) {
            return retVal.getLast();
        } else {
            for (Bundle nextVersion : retVal) {
                String nextVersionId = nextVersion.getIdElement().getVersionIdPart();
                if (theId.getVersionIdPart().equals(nextVersionId)) {
                    return nextVersion;
                }
            }
            // No matching version
            throw new ResourceNotFoundException("Unknown version: " + theId.getValue());
        }

    }

    @History()
    public Deque<Bundle> historyBundle(@IdParam IdType theId) {
        Deque<Bundle> retVal;
        try {
            retVal = bundleRes.get(theId.getIdPartAsLong());
        } catch (NumberFormatException e) {
            /*
             * If we can't parse the ID as a long, it's not valid so this is an unknown resource
             */
            throw new ResourceNotFoundException(theId);
        }
        return retVal;
    }

    /**
     * The "@Update" annotation indicates that this method supports replacing an existing
     * resource (by ID) with a new instance of that resource.
     *
     * @param theId     This is the ID of the Bundle to update
     * @param theBundle This is the actual resource to save
     * @return This method returns a "MethodOutcome"
     */
    @Update()
    public MethodOutcome updateBundle(@IdParam IdType theId, @ResourceParam Bundle theBundle) {

        Long id;
        try {
            id = theId.getIdPartAsLong();
        } catch (DataFormatException e) {
            throw new InvalidRequestException("Invalid ID " + theId.getValue() + " - Must be numeric");
        }

        /*
         * Throw an exception (HTTP 404) if the ID is not known
         */
        if (!bundleRes.containsKey(id)) {
            throw new ResourceNotFoundException(theId);
        }

        String versionId = addNewVersion(theBundle, id);

//		return new MethodOutcome();
        MethodOutcome retVal = new MethodOutcome();
        retVal.setId(new IdType("Bundle", String.valueOf(id), versionId));

        OperationOutcome outcome = new OperationOutcome();
        outcome.setIdElement(new IdType("Bundle", String.valueOf(id), versionId));
        outcome.addIssue().setDiagnostics("更新Bundle成功").setId(String.valueOf(id));
        retVal.setOperationOutcome(outcome);

        return retVal;
    }

    @Delete
    public MethodOutcome deleteBundle(@IdParam IdType theId) {
        Long id;
        try {
            id = theId.getIdPartAsLong();
        } catch (DataFormatException e) {
            throw new InvalidRequestException("Invalid ID " + theId.getValue() + " - Must be numeric");
        }

        /*
         * Throw an exception (HTTP 404) if the ID is not known
         */
        if (!bundleRes.containsKey(id)) {
            throw new ResourceNotFoundException(theId);
        }

        bundleRes.remove(id);

//		return new MethodOutcome();
        MethodOutcome retVal = new MethodOutcome();

        OperationOutcome outcome = new OperationOutcome();
        outcome.addIssue().setDiagnostics("删除Bundle成功").setId(String.valueOf(id));
        retVal.setOperationOutcome(outcome);

        return retVal;
    }
}
